# -*- coding:utf-8 -*-
import ctypes
import threading
import MySQLdb
import os
import socket
import json
import httplib
import urllib2
import uuid
import zipfile
import base64
import sys
import hashlib
import datetime
from time import *


def get_appid(url):
    index = url.rfind('id')
    index_right = url.find('?', index)
    if(index_right == -1):
        appid = url[index + 2: len(url)]
    else:
        appid = url[index + 2: index_right]
    return appid

def load_all_useraccount():
    global all_useraccount
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    all_useraccount = list()
    f = open('itunes_account_hot.txt' , 'r')
    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        #关闭连接，释放资源   
        while True:
            record = f.readline()
            if(record == ''):
                break
            tmp_account = record.strip('\n')

            strlist    = tmp_account.split('|')
            strlist_1  = strlist[0]

            cur  = conn.cursor()
            sql  = 'select count(*) totalcount from t_xyapplist_hot where useraccount="%s" limit 1;'%(strlist_1)
            cur.execute(sql)
            result = cur.fetchone()
            if result[0] < 8000:
                all_useraccount.append(tmp_account+"|"+str(result[0]))
        cur.close()
        conn.close()
        return all_useraccount
    except MySQLdb.Error, e:
        print "Mysql Error:", e
        sys.exit(0)

def load_all_urls():
    global all_urls
    all_urls = list()
    f = open('itunesurl_hot.txt', 'r')
    while True:
        record = f.readline()
        if (record == ''):
            break
        all_urls.append(record.strip('\n'))
    return all_urls

def buy_app(appid, referUrl, account, password):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    host = '127.0.0.1'
    port = 1234
    s.connect((host, port))
    print('connect xigeserver succeed\n')
    data = {'appid': appid, 'referUrl': referUrl, 'appleId': account,'password': password}
    try:
        str_data = json.dumps(data)
        s.send(str_data + '\r\n\r\n')
        buf = s.recv(4096*10)
        s.close()
        decodejson = json.loads(buf)
        return decodejson['result'], decodejson['szUrl'], decodejson['szKey'], decodejson['szSinf'], decodejson['szArtworkUrl'], decodejson['szMetadataPath'], decodejson['szVersion']
    except Exception, e:
        return False, '', '', '', '', '', ''

def download_app(url, key, appid):
    global download_thread_num
    try:
        
        download_thread_num = download_thread_num + 1
        print str(download_thread_num) + ' download nums'
        
        cookie = 'downloadKey=%s'%key
        req = urllib2.Request(url)
        req.add_header('Cookie', cookie)
        req.add_header('User-Agent', 'Mozilla/4.0 (comatible; MSIE 8.0; Win32)')
        req.add_header('Connection', 'Keep-Alive')
        req.add_header('Cache-Control', 'no-cache')
        resp = urllib2.urlopen(req)
        content = resp.read()
        resp.close()
        if not os.path.exists('ipas'):
            os.mkdir('ipas')

        m = hashlib.md5(str(appid))
        m.digest()
        md5  = m.hexdigest()
        str1 = md5[0: 2]
        str2 = md5[2: 4]
        if not os.path.exists('ipas/'+str1):
            os.mkdir('ipas/'+str1)
        if not os.path.exists('ipas/'+str1+"/"+str2):
            os.mkdir('ipas/'+str1+"/"+str2)
        random_filename = 'ipas/'+ str1 + "/" + str2 + "/" + str(md5) + str2 + str1 + '.ipa'
        # print(random_filename)
        f = open(random_filename, 'wb')
        f.write(content)
        f.close()
        if(download_thread_num > 1):
            download_thread_num = download_thread_num -1
        return random_filename
    except:
        if(download_thread_num > 1):
            download_thread_num = download_thread_num -1
        print('happen an exception when download app url is %s key is %s'%(url, key))
        return ""

def download_png(url):
    try:
        req = urllib2.Request(url)
        resp = urllib2.urlopen(req)
        content = resp.read()
        resp.close()
        return True, content
    except:
        print('happen an exception when download png the url is %s'%url)
        return False, ""

def make_newipa(ipaname, png_content, sinf_content, appid, accounthash):
    try:
        zfobj = zipfile.ZipFile(ipaname)
        for name in zfobj.namelist():
            name = name.replace('\\', '/')
            if name.find('SC_Info/') > 0 and not name.endswith('SC_Info/'):
                basename = os.path.basename(name)
                break
        dotindex = basename.find('.')
        newsinfname = basename[0:dotindex] + '.sinf'
        newsinfpath = os.path.dirname(name) + '/' + newsinfname
        zfobj.close()

        # print('newsinfpath is %s'%newsinfpath)
        zf = zipfile.ZipFile(ipaname, "a")

        m = hashlib.md5(appid)
        m.digest()
        md5 = m.hexdigest()
        str1 = md5[0: 2]
        str2 = md5[2: 4]

        a_m = hashlib.md5(accounthash)
        a_m.digest()
        a_md5 = a_m.hexdigest()

        filename  = 'ipas/' + str1 +  '/' + str2 + '/' + str(uuid.uuid4())

        tempFile = open(filename, 'wb')
        base64_content = base64.decodestring(sinf_content)
        tempFile.write(base64_content)
        tempFile.close() 
        zf.write(filename, newsinfpath)

        tempFile = open(filename, 'wb')
        tempFile.write(png_content)
        tempFile.close() 
        zf.write(filename, 'iTunesArtwork')
        os.remove(filename)

        plist_name = 'c:/iTunes/'+str(appid)+'.plist'
        if not os.path.exists(plist_name):
            return False
        else:
            fplist = open(plist_name , 'r')
            meta_path = ''
            while 1:
                line = fplist.readline()
                if not line:
                    break
                else:
                    meta_path += line
            fplist.close()
            os.remove(plist_name)

            tempFile = open(filename, 'wb')
            meta_path = meta_path.split("</dict>")
            meta_path_new = ''
            count = 0
            for sub_data in meta_path:
                if len(meta_path) - 1 != count:
                    if len(meta_path) - 2 == count :
                        meta_path_new = meta_path_new + sub_data+"<key>account hash</key><string>"+a_md5+"</string></dict></plist>"
                    else:
                        meta_path_new = meta_path_new + sub_data+"</dict>"
                    count = count + 1
            if(meta_path_new == ''):
                meta_path_new = meta_path
            tempFile.write(meta_path_new)
            tempFile.close() 
            zf.write(filename, 'iTunesMetadata.plist')
            os.remove(filename)

            zf.close()
            return True
    except:
        print('when make_newipa happen an error')
        return False

def sec2date(secs):
    return strftime("%Y-%m-%d %H:%M:%S", localtime(secs))

def save_app_info(appid, state, appname, url, cookie, orign_url, version, account, bundleId):
    global sqlite_mutex
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    sqlite_mutex.acquire()
    try:
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        # is_install 表示有 正版安装包
        sql  = 'update t_xyapplist_hot set status=%d, useraccount="%s",is_install=1,bundleid="%s" where itunesid="%s"'%(state, account, bundleId,appid)
        print(sql)
        cur.execute(sql)
        conn.commit()
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e
    sqlite_mutex.release()


def del_buy_free_appp(appid):
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        sql  = 'delete from t_xyapplist_hot where itunesid="%s" limit 1;'%(appid)
        print(sql)
        cur.execute(sql)
        conn.commit()
        #关闭连接，释放资源   
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e

def load_all_urls_DB():
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    if os.path.isfile('itunesurl_hot.txt'):
        os.remove('itunesurl_hot.txt')

    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        # status 2 ： 新增 3 ： 更新
        # sql  = "select itunesid,itunes from t_xyapplist_hot where status=2 or status = 3;";
        sql  = "select itunesid,itunes from t_xyapplist_hot where status=2 and bundleid = '';";
        cur.execute(sql)
        result = cur.fetchall()

        fp = open('itunesurl_hot.txt', 'wb')

        for f in result:
            fp.write(f[1] + "\n")

        conn.commit()
        #关闭连接，释放资源   
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e

def get_bundleid(path , itunesid):
    if not os.path.exists(path):
        return 1
    else:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)       
        host = '127.0.0.1'
        port = 5678
        s.connect((host, port))
        data = {'appid': str(itunesid), 'pathurl': "F:/app_up/"+path}
        str_data = json.dumps(data)
        print str_data
        s.send(str_data + '\r\n\r\n')
        buf = s.recv(4096*10)
        s.close()
        decodejson = json.loads(buf)
        bundleId = decodejson['bundleId']
        print (bundleId)
        return bundleId

def thread_main(arg):
    global all_urls
    global all_account_cur
    global all_password_cur
    global all_account_cur_flag # 记录是否已经切换请求帐号
    global all_account_cur_num  # 记录当前帐号 总的请求次数

    while True:
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        #加入线程锁
        threadname = threading.currentThread().getName()
        urls_mutex.acquire()
        if len(all_urls):
            url = all_urls.pop(0)
            appid = get_appid(url)
        else:
            urls_mutex.release()
            break
        if(all_account_cur==""):
            if len(all_useraccount):
                account_cur = all_useraccount.pop(0)
                strlist = account_cur.split('|')
                all_account_cur  = strlist[0]
                all_password_cur = strlist[1]
                all_account_cur_num = int(strlist[2])
            else:
                urls_mutex.release()
                break
        urls_mutex.release()
        print('current account : ' + all_account_cur +" ---  threadname: " + threadname +"\n")
        retvalue = buy_app(appid, url, all_account_cur, all_password_cur)
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        # print retvalue
        print('buyapp ret 1'+ " : " + datetime + " == " + str(all_account_cur_num))
        all_account_cur_num = all_account_cur_num + 1
        #  `status` '0未购买 1购买成功 2ipa下载成功 3购买失败 4ipa下载失败 5 下载png失败 6 make_newipa ipa成功（这是才是最终的完成） 7make_newipa失败',
        # 返回值 1 成功 ，2004 帐号被禁用
        print retvalue[0]
        if retvalue[0] == '1':
            print('buy app succeed now download app')
            ipaname = download_app(retvalue[1], retvalue[2], appid)
            print('download app over')
            if ipaname == '':
                # save_app_info(appid, 4, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                all_urls.append(url)
                continue
            (result,png_content) = download_png(retvalue[4])
            if not result:
                # save_app_info(appid, 5, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                all_urls.append(url)
                continue
            if make_newipa(ipaname, png_content, retvalue[3], appid, all_account_cur):
                # 获取 bundleid
                bundleId = get_bundleid(ipaname , appid)
                if bundleId == 1:
                    bundleId = get_bundleid(ipaname , appid)
                    if bundleId == 1:
                        bundleId = ''

                save_app_info(appid, 6, ipaname, retvalue[1], retvalue[2], url, retvalue[6], all_account_cur, bundleId) #这里把 app 返回的信 成功打入 ipa 整个过程才会成功 

                if all_account_cur_num >= 8000 : # 下载量大于 1000 则更换帐号
                    urls_mutex.acquire()
                    if len(all_useraccount):
                        account_cur = all_useraccount.pop(0)
                        strlist     = account_cur.split('|')
                        all_account_cur  = strlist[0]
                        all_password_cur = strlist[1]
                        all_account_cur_flag = 1
                        all_account_cur_num  = int(strlist[2])
                    else:
                        urls_mutex.release()
                        break
                    urls_mutex.release()
                    print ("new account is : "+all_account_cur+" ---  threadname: " + threadname +"\n")

                if(all_account_cur_num > 50 and all_account_cur_flag == 1): # 最多开 10 的线程， 极端情况下 所有的线程使用的禁用帐号都跑完了 ， 重置 表示位
                    all_account_cur_flag = 0
                    print 'all_account_cur_flag is 0'
            else:
                # save_app_info(appid, 7, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                all_urls.append(url)
        elif retvalue[0]== '2004':
            all_urls.append(url) # 从新加回队列 继续下载 帐号被禁用
            print(all_account_cur+' is disabled'+" ---  threadname: " + threadname +"\n")
            if(all_account_cur_flag == 0):
                urls_mutex.acquire()
                if len(all_useraccount):
                    account_cur = all_useraccount.pop(0)
                    strlist     = account_cur.split('|')
                    all_account_cur  = strlist[0]
                    all_password_cur = strlist[1]
                    all_account_cur_flag = 1
                    all_account_cur_num = int(strlist[2])
                else:
                    urls_mutex.release()
                    break
                urls_mutex.release()
                print ("new account is : "+all_account_cur+" ---  threadname: " + threadname +"\n")
            continue
        elif retvalue[0] == '-2':
            all_urls.append(url) # 查询 server 失败
            continue
        elif  retvalue[0] == '-13':#限时免费 开始收费了 删除免费行列
            del_buy_free_appp(appid)
            continue
        elif retvalue[0] == '-14': #商品开始下架
            del_buy_free_appp(appid)
            continue
    print('thread exit')
    
def main():
    print('start')
    global sqlite_mutex
    global urls_mutex
    global all_account_cur
    global all_password_cur
    global all_account_cur_flag
    global all_account_cur_num
    global download_thread_num

    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name

    db_host  = "127.0.0.1"
    db_user  = 'root'
    db_passwd = ''
    db_port = 3306
    db_name = "app_xyapplist"

    download_thread_num = 0
    all_account_cur = ''
    all_password_cur = ''
    all_account_cur_flag = 0
    all_account_cur_num  = 0

    load_all_urls_DB()
    load_all_useraccount()
    socket.setdefaulttimeout(600)
    load_all_urls()
    
    threads = []
    urls_mutex = threading.Lock()
    sqlite_mutex = threading.Lock()
    for x in xrange(0, 25):
        threads.append(threading.Thread(target=thread_main, args=(25,)))
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    print('end')
    
        
if __name__ == '__main__':
    reload(sys) 
    sys.setdefaultencoding('utf8')
    main()
